#include "AudioIn.h"
#ifndef AF_IPX
#include <windows.h>
#endif
#include "Header.h"
#include "Mutex.h"
#include "Threadpool.h"

AudioIn::AudioIn(IBuffer *buf, unsigned int ALBufferSize) :
_alBufferSize(ALBufferSize),
_isRunning(false)
{
	this->_deviceList = NULL;
	this->_deviceTable = NULL;
	this->_currentDevice = NULL;
	this->_inRun = false;
	this->_sleepReactivity = 10;
	this->_buf = buf;
	this->_samples = new char[_alBufferSize];
	_mutex = new Mutex();
}

AudioIn::~AudioIn()
{
	this->Stop();
	Sleep(200);  // TODO URGENT ... bug de synchro dans les delete depuis l'export C
	delete _mutex;
	delete this->_deviceList;
	delete[] this->_deviceTable;
	delete[] _samples;
	if (_currentDevice != NULL)
	{
		ALCboolean ok = alcCaptureCloseDevice(_currentDevice);
		if (ok == ALC_FALSE)
			throw "error";
		
	}
}

char **AudioIn::DetectDevice()
{
	if(alcIsExtensionPresent(NULL, "ALC_ENUMERATE_ALL_EXT"))
	{
		int numDevice = 0;
		char *ptr;
		if (this->_deviceList != NULL)
		{
			delete this->_deviceList;
		}
		this->_deviceList = (char*)alcGetString
		(
			NULL, 
			ALC_ALL_DEVICES_SPECIFIER
		);
		if (this->_deviceList == "")
		{
			return (char **)-1;
		}
		ptr = this->_deviceList;
		while( ptr && *ptr )
		{
			while (*ptr != NULL)
				++ptr;
			ptr++;
			numDevice++;
		}
		if (this->_deviceTable != NULL)
		{
			delete [] this->_deviceTable;
		}
		this->_deviceTable = new char *[numDevice];
		ptr = this->_deviceList;
		numDevice = 0;
		while( ptr && *ptr )
		{
			this->_deviceTable[numDevice] = ptr;
			while (*ptr != NULL)
				++ptr;
			ptr++;
			numDevice++;
		}
		this->_deviceTable[numDevice] = NULL;
	}
	else
	{
		return NULL;
	}
	return this->_deviceTable;
}

void AudioIn::SelectInDevice(char *deviceName, unsigned short frequence, char format)
{
	ALenum _format;

	if (format == STEREO16)
		_format = AL_FORMAT_STEREO16;
	else if  (format == STEREO8)
		_format = AL_FORMAT_STEREO8;
	else if  (format == MONO16)
		_format = AL_FORMAT_MONO16;
	else
		_format = AL_FORMAT_MONO8;
	this->_freq = frequence;
	this->_format = format;
	if (this->_currentDevice != NULL)
	{
		Stop();
		alcCaptureCloseDevice(this->_currentDevice);
		this->_currentDevice = NULL;
	}
	_sampleSize = (format == STEREO16 ? 4 : (format != MONO8 ? 2 : 1));
	this->_currentDevice = alcCaptureOpenDevice
	(
		deviceName,
		frequence,
		_format,
		_alMaxSample = this->_alBufferSize / _sampleSize
	);

	if (_inRun)
		Start();
}

void AudioIn::SelectInDevice(int deviceId, unsigned short frequence, char format)
{
	ALenum _format;

	if (format == STEREO16)
		_format = AL_FORMAT_STEREO16;
	else if  (format == STEREO8)
		_format = AL_FORMAT_STEREO8;
	else if  (format == MONO16)
		_format = AL_FORMAT_MONO16;
	else
		_format = AL_FORMAT_MONO8;
	this->_freq = frequence;
	this->_format = format;
	if (this->_currentDevice != NULL)
	{
		Stop();
		alcCaptureCloseDevice(this->_currentDevice);
		this->_currentDevice = NULL;
	}
	_sampleSize = (format == STEREO16 ? 4 : (format != MONO8 ? 2 : 1));
	this->_currentDevice = alcCaptureOpenDevice(
		this->_deviceTable[deviceId],
		frequence,
		_format,
		_alMaxSample = this->_alBufferSize / _sampleSize
		);
	if (_inRun)
		Start();
}

void AudioIn::AutoSelectInDevice(unsigned short frequence, char format)
{
	ALenum _format;

	if (format == STEREO16)
		_format = AL_FORMAT_STEREO16;
	else if  (format == STEREO8)
		_format = AL_FORMAT_STEREO8;
	else if  (format == MONO16)
		_format = AL_FORMAT_MONO16;
	else
		_format = AL_FORMAT_MONO8;
	this->_freq = frequence;
	this->_format = format;
	if (this->_currentDevice != NULL)
	{
		Stop();
		alcCaptureCloseDevice(this->_currentDevice);
	}
	_sampleSize = (format == STEREO16 ? 4 : (format != MONO8 ? 2 : 1));
	this->_currentDevice = alcCaptureOpenDevice(
		NULL,
		frequence,
		_format,
		_alMaxSample = this->_alBufferSize /_sampleSize
		);

	if (_inRun)
		Start();
}

void AudioIn::Start()
{
	ThreadPool::Request *req;

	if (this->_inRun)
		return;
	this->_inRun = true;
	req = new ThreadPool::Request();

	req->autoFree = true;
	req->parameters = this;
	req->function = (ThreadPool::callback)AudioIn::runIn;
	ThreadPool::GetThreadPool()->AddRequest(req);
}

void AudioIn::runIn(AudioIn *audioIn)
{
	
	std::vector<ALshort>::iterator	it;
	std::vector<ALshort>::iterator	end;

	audioIn->_mutex->Lock();
	audioIn->_isRunning = true;
	if (!audioIn->_currentDevice)
		throw "TODO BETA 2 GESTION ERROR No Input Device";
	alcCaptureStart(audioIn->_currentDevice);
	
	while (audioIn->_inRun && audioIn->_currentDevice)
	{
		ALCint samplesAvailable;

		alcGetIntegerv
		(
			audioIn->_currentDevice, 
			ALC_CAPTURE_SAMPLES, 
			1,
			&samplesAvailable
		);

		if (samplesAvailable > 0)		
		{
			samplesAvailable = samplesAvailable < audioIn->_alMaxSample ? samplesAvailable : audioIn->_alMaxSample;
			alcCaptureSamples
			(
				audioIn->_currentDevice, 
				audioIn->_samples,
				samplesAvailable
			);
			audioIn->_buf->Write(audioIn->_samples, samplesAvailable * audioIn->_sampleSize);
		}
		Sleep(audioIn->_sleepReactivity);

	}
	alcCaptureStop(audioIn->_currentDevice);
	audioIn->_isRunning = false;
	
	audioIn->_mutex->Unlock();
}


void AudioIn::Stop()
{
	if (!_isRunning)
		return;
	this->_inRun = false;
	this->_mutex->Lock();
	this->_mutex->Unlock();
}

unsigned short AudioIn::GetFrequence()
{
	return this->_freq;
}

char AudioIn::GetFormat()
{
	return this->_format;
}

IBuffer *AudioIn::GetBuffer()
{
	return this->_buf;
}